import { useCallback, useContext, useEffect, useRef, useState, Fragment } from 'react';
import { PortalContext } from '@/components/PortalProvider';
import { nanoid } from 'nanoid';
import React from 'react';

// 基础的 Overlay 属性
export interface BaseOverlayProps {
  open?: boolean;
  onClose?: (...args: any[]) => void;
}

// 辅助类型，用于处理组件的 Props
type OverlayProps<T> = T extends { onClose: infer U }
  ? Omit<T, 'onClose'> & { onClose?: U }
  : T;

// 打开 Overlay 的配置项
export interface OpenOverlayProps<T = object> {
  // 用于标识打开实例
  name?: string;
  // Overlay 组件
  component: React.ComponentType<T>;
  // 传给 Overlay 组件的属性
  props: OverlayProps<T>;
  // 控制显示的属性名，默认为 open
  openProp?: string;
  // 关闭后是否保持状态
  keepAlive?: boolean;
  // 是否强制使用 Portal
  usePortal?: boolean;
  // 是否独立存在（组件卸载后不会被关闭）
  standalone?: boolean;
}

// 内部使用的 Overlay 类型
interface OverlayInstance<T = any> {
  id: string;
  name?: string;
  component: React.ComponentType<T>;
  props: T;
  openProp: string;
  keepAlive?: boolean;
  usePortal?: boolean;
  standalone?: boolean;
  isOpen: boolean;
  inPortal: boolean;
  resolve?: (value: unknown) => void;
  reject?: (reason?: any) => void;
}

const ANIMATION_DURATION = 300;

export function useOverlay() {
  const overlaysRef = useRef<OverlayInstance[]>([]);
  const [localOverlays, setLocalOverlays] = useState<OverlayInstance[]>([]);
  const portalContext = useContext(PortalContext);

  if (!portalContext) {
    throw new Error("useOverlay must be used within a PortalProvider");
  }

  const { addToPortal, removeFromPortal } = portalContext;

  // 处理关闭事件
  const handleClose = useCallback(
    (overlay: OverlayInstance, ...args: any[]) => {
      const { id, props, openProp, keepAlive, inPortal } = overlay;

      overlay.isOpen = false;

      // 更新 props 中的开关属性
      if (openProp) {
        props[openProp] = false;
      }

      // 如果在 Portal 中，更新 Portal
      if (inPortal) {
        const element = React.createElement(overlay.component, {
          ...props,
        });
        addToPortal(id, element);
      } else {
        // 更新本地状态
        setLocalOverlays(state =>
          state.map(item => item.id === id ? { ...overlay } : item)
        );
      }

      // 调用原始的 onClose
      if (props.onClose) {
        props.onClose(...args);
      }

      // 解决 Promise
      if (overlay.resolve) {
        overlay.resolve(args[0]);
      }

      // 如果不需要保持状态，延迟移除
      if (!keepAlive) {
        setTimeout(() => {
          if (overlay.isOpen) return;

          overlaysRef.current = overlaysRef.current.filter(item => item.id !== id);
          
          if (inPortal) {
            removeFromPortal(id);
          } else {
            setLocalOverlays(state => state.filter(item => item.id !== id));
          }
        }, ANIMATION_DURATION);
      }
    },
    [addToPortal, removeFromPortal]
  );

  // 打开 Overlay
  const open = useCallback(
    <T extends object>(options: OpenOverlayProps<T>) => {
      return new Promise<unknown>((resolve, reject) => {
        const {
          name,
          component,
          props,
          openProp = 'open',
          keepAlive = false,
          usePortal = true,
          standalone = false,
        } = options;

        // 查找现有实例或创建新实例
        const existingOverlay = overlaysRef.current.find(item =>
          name ? item.name === name : item.keepAlive && !item.name && item.component === component
        );

        const overlay: OverlayInstance<T> = existingOverlay || {
          id: nanoid(),
          name,
          component,
          props,
          openProp,
          keepAlive,
          usePortal,
          standalone,
          isOpen: true,
          inPortal: usePortal,
          resolve: undefined,
          reject: undefined,
        };

        const isNew = !existingOverlay;
        if (isNew) {
          overlaysRef.current.push(overlay);
        }

        // 更新 props
        overlay.props = {
          ...props,
          [openProp]: true,
          onClose: (...args: any[]) => handleClose(overlay, ...args),
        };

        overlay.resolve = resolve;
        overlay.reject = reject;

        // 决定是否使用 Portal
        if (usePortal || standalone) {
          if (!isNew && !overlay.inPortal) {
            setLocalOverlays(state => state.filter(item => item.id !== overlay.id));
          }
          overlay.inPortal = true;
          const element = React.createElement(overlay.component, overlay.props);
          addToPortal(overlay.id, element);
        } else {
          if (!isNew && overlay.inPortal) {
            removeFromPortal(overlay.id);
          }
          overlay.inPortal = false;
          setLocalOverlays(state =>
            isNew
              ? [...state, overlay]
              : state.map(item => (item.id === overlay.id ? overlay : item))
          );
        }
      });
    },
    [addToPortal, removeFromPortal, handleClose]
  );

  // 关闭指定的 Overlay
  const close = useCallback(
    (nameOrComponent: string | React.ComponentType<any>) => {
      if (typeof nameOrComponent === 'string') {
        const overlay = overlaysRef.current.find(item => item.name === nameOrComponent);
        if (overlay?.isOpen) {
          handleClose(overlay);
        }
      } else {
        overlaysRef.current.forEach(overlay => {
          if (overlay.isOpen && !overlay.name && overlay.component === nameOrComponent) {
            handleClose(overlay);
          }
        });
      }
    },
    [handleClose]
  );

  // 关闭所有 Overlay
  const closeAll = useCallback(() => {
    overlaysRef.current.forEach(overlay => {
      if (overlay.isOpen) {
        handleClose(overlay);
      }
    });
  }, [handleClose]);

  // 检查 Overlay 是否打开
  const isOpen = useCallback((nameOrComponent: string | React.ComponentType<any>) => {
    return overlaysRef.current.some(overlay =>
      overlay.isOpen && (
        typeof nameOrComponent === 'string'
          ? overlay.name === nameOrComponent
          : overlay.component === nameOrComponent
      )
    );
  }, []);

  // 渲染本地 Overlays 的容器组件
  const OverlayContainer: React.FC = useCallback(() => {
    return (
      <>
        {localOverlays.map(overlay => (
          <Fragment key={overlay.id}>
            {React.createElement(overlay.component, overlay.props)}
          </Fragment>
        ))}
      </>
    );
  }, [localOverlays]);

  // 清理 effect
  useEffect(() => {
    return () => {
      overlaysRef.current.forEach(overlay => {
        if (overlay.inPortal && !overlay.standalone) {
          removeFromPortal(overlay.id);
        }
      });
    };
  }, [removeFromPortal]);

  return {
    open,
    close,
    closeAll,
    isOpen,
    OverlayContainer,
  };
} 